﻿using System;
using System.Runtime.CompilerServices;
using Internal.KuiHuaBaoDian.Services.Timer;

namespace KuiHuaBaoDian.Services.Timer {

    public interface ITimer : ITimerData {

        public delegate void DataDelegate(ITimerData data);

        event DataDelegate Timer;

        event DataDelegate Completed;

        event DataDelegate TimerStart;

        TimerHandle Handle { get; }

        void Update(float delta);

        void Reset();

        void Start();
    }

    public interface ITimerData {

        float Time { get; }

        float Current { get; }

        uint Count { get; }

        bool IsIgnoreScale { get; }

        float Scale { get; }

        bool IsComplete { get; }

        bool IsAutoKill { get; }

        bool IsPausing { get; }

        uint Times { get; }
    }

    public interface ITimerSetter {

        void SetIsIgnoreScale(bool value);

        void SetTime(float value);

        void SetCount(uint value);

        void SetIsAutoKill(bool value);

        void SetIsPause(bool value);

        void SetScale(float value);
    }

    [Implement(typeof(ITimer))]
    internal sealed class KHBD_Timer : KHBD_Object, ITimer, ITimerSetter, ITimerServiceInternal.IReusable {

        public event ITimer.DataDelegate Timer;

        public event ITimer.DataDelegate Completed;

        public event ITimer.DataDelegate TimerStart;

        public TimerHandle Handle { get; private set; } = new TimerHandle { GUID = Guid.NewGuid() };

        public float Time { get; private set; } = 1.0f;

        public float Current { get; private set; } = 0.0f;

        public uint Count { get; private set; } = 0;

        public bool IsIgnoreScale { get; private set; } = false;

        public float Scale { get; private set; } = 1.0f;

        public bool IsComplete { get; private set; } = false;

        public bool IsAutoKill { get; private set; } = true;

        public bool IsPausing { get; private set; } = false;

        public uint Times { get; private set; } = 0;

        public void Reset() {
            IsComplete = false;
            Current = 0.0f;
            IsPausing = false;
            Times = 0;
        }

        public void Start() {
            TimerStart?.Invoke(this);
        }

        public void SetTime(float value) {
            Time = value >= 0 ? value : 0.0f;
        }

        public void SetCount(uint value) {
            Count = value;
        }

        public void SetIsAutoKill(bool value) {
            IsAutoKill = value;
        }

        void ITimerSetter.SetIsIgnoreScale(bool value) {
            IsIgnoreScale = value;
        }

        void ITimerSetter.SetIsPause(bool value) {
            IsPausing = value;
        }

        void ITimerSetter.SetScale(float value) {
            Scale = value;
        }

        void ITimerServiceInternal.IReusable.Reuse() => Reuse();
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void Reuse() {
            Handle = new TimerHandle { GUID = Guid.NewGuid() };
            Scale = 1.0f;
            Reset();
        }

        void ITimerServiceInternal.IReusable.Recycle() => Recycle();
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void Recycle() {
            Timer = null;
            Completed = null;
            TimerStart = null;
            Handle = TimerHandle.None;
        }

        protected override void Dispose() {
            Recycle();
        }

        public void Update(float delta) {
            if (!IsComplete && !IsPausing) {
                var sacle = Scale >= 0 ? Scale : 0;
                if (sacle > 0) {
                    delta *= sacle;
                    Current += delta;
                    if (Current >= Time) {
                        Current -= Time;
                        Times++;
                        Timer?.Invoke(this);
                        if (Count != 0 && Times >= Count) {
                            IsComplete = true;
                            Completed?.Invoke(this);
                        } else {
                            TimerStart?.Invoke(this);
                        }
                    }
                }
            }
        }
    }
}
